home *** CD-ROM | disk | FTP | other *** search
/ Hardcore Visual Basic 5.0 (2nd Edition) / Hardcore Visual Basic 5.0 - Second Edition (1997)(Microsoft Press).iso / Code / subclass.bas < prev    next >
BASIC Source File  |  1997-06-14  |  5KB  |  150 lines

  1. Attribute VB_Name = "MSubclass"
  2. Option Explicit
  3.  
  4. ' SubTimer is independent of VBCore, so it hard codes error handling
  5.  
  6. Public Enum EErrorWindowProc
  7.     eeBaseWindowProc = 13080 ' WindowProc
  8.     eeCantSubclass           ' Can't subclass window
  9.     eeAlreadyAttached        ' Message already handled by another class
  10.     eeInvalidWindow          ' Invalid window
  11.     eeNoExternalWindow       ' Can't modify external window
  12. End Enum
  13.  
  14. Private Sub ErrRaise(e As Long)
  15.     Dim sText As String, sSource As String
  16.     If e > 1000 Then
  17.         sSource = App.EXEName & ".WindowProc"
  18.         Select Case e
  19.         Case eeCantSubclass
  20.             sText = "Can't subclass window"
  21.         Case eeAlreadyAttached
  22.             sText = "Message already handled by another class"
  23.         Case eeInvalidWindow
  24.             sText = "Invalid window"
  25.         Case eeNoExternalWindow
  26.             sText = "Can't modify external window"
  27.         End Select
  28.         Err.Raise e Or vbObjectError, sSource, sText
  29.     Else
  30.         ' Raise standard Visual Basic error
  31.         Err.Raise e, sSource
  32.     End If
  33. End Sub
  34.  
  35. Sub AttachMessage(iwp As ISubclass, ByVal hWnd As Long, _
  36.                   ByVal iMsg As Long)
  37.     Dim procOld As Long, f As Long, c As Long
  38.     ' Validate window
  39.     If IsWindow(hWnd) = False Then ErrRaise eeInvalidWindow
  40.     If IsWindowLocal(hWnd) = False Then ErrRaise eeNoExternalWindow
  41.  
  42.     ' Get the message count
  43.     c = GetProp(hWnd, "C" & hWnd)
  44.     If c = 0 Then
  45.         ' Subclass window by installing window procecure
  46.         procOld = SetWindowLong(hWnd, GWL_WNDPROC, AddressOf WindowProc)
  47.         If procOld = 0 Then ErrRaise eeCantSubclass
  48.         ' Associate old procedure with handle
  49.         f = SetProp(hWnd, hWnd, procOld)
  50.         BugAssert f <> 0
  51.         ' Count this message
  52.         c = 1
  53.         f = SetProp(hWnd, "C" & hWnd, c)
  54.     Else
  55.         ' Count this message
  56.         c = c + 1
  57.         f = SetProp(hWnd, "C" & hWnd, c)
  58.     End If
  59.     BugAssert f <> 0
  60.     ' This message had better not be already attached
  61.     If GetProp(hWnd, hWnd & "#" & iMsg) <> pNull Then
  62.         ErrRaise eeAlreadyAttached
  63.     End If
  64.     ' Associate object with message (one per handle)
  65.     f = SetProp(hWnd, hWnd & "#" & iMsg, ObjPtr(iwp))
  66.     BugAssert f <> 0
  67. End Sub
  68.  
  69. Sub DetachMessage(iwp As ISubclass, ByVal hWnd As Long, _
  70.                   ByVal iMsg As Long)
  71.     Dim procOld As Long, f As Long, c As Long
  72.     ' Get the message count
  73.     c = GetProp(hWnd, "C" & hWnd)
  74.     If c = 1 Then
  75.         ' This is the last message, so unsubclass
  76.         procOld = GetProp(hWnd, hWnd)
  77.         BugAssert procOld <> pNull
  78.         ' Unsubclass by reassigning old window procedure
  79.         Call SetWindowLong(hWnd, GWL_WNDPROC, procOld)
  80.         ' Remove unneeded handle (oldProc)
  81.         RemoveProp hWnd, hWnd
  82.         ' Remove unneeded count
  83.         RemoveProp hWnd, "C" & hWnd
  84.     Else
  85.         ' Uncount this message
  86.         c = GetProp(hWnd, "C" & hWnd)
  87.         c = c - 1
  88.         f = SetProp(hWnd, "C" & hWnd, c)
  89.     End If
  90.     ' Remove unneeded message (subclass object pointer)
  91.     RemoveProp hWnd, hWnd & "#" & iMsg
  92. End Sub
  93.  
  94. Private Function WindowProc(ByVal hWnd As Long, ByVal iMsg As Long, _
  95.                             ByVal wParam As Long, ByVal lParam As Long) _
  96.                             As Long
  97.     Dim procOld As Long, pSubclass As Long, f As Long
  98.     Dim iwp As ISubclass, iwpT As ISubclass
  99.     ' Get the old procedure from the window
  100.     procOld = GetProp(hWnd, hWnd)
  101.     BugAssert procOld <> pNull
  102.     ' Get the object pointer from the message
  103.     pSubclass = GetProp(hWnd, hWnd & "#" & iMsg)
  104.     If pSubclass = pNull Then
  105.         ' This message not handled, so pass on to old procedure
  106.         WindowProc = CallWindowProc(procOld, hWnd, iMsg, _
  107.                                     wParam, ByVal lParam)
  108.         Exit Function
  109.     End If
  110.     
  111.     ' Turn the pointer into an illegal, uncounted interface
  112.     CopyMemory iwpT, pSubclass, 4
  113.     ' Do NOT hit the End button here! You will crash!
  114.     BugMessage "Got object"
  115.     ' Assign to legal reference
  116.     Set iwp = iwpT
  117.     ' Still do NOT hit the End button here! You will still crash!
  118.     ' Destroy the illegal reference
  119.     CopyMemory iwpT, 0&, 4
  120.     ' OK, hit the End button if you must--you'll probably still crash,
  121.     ' but it will be because of the subclass, not the uncounted reference
  122.     
  123.     ' Use the interface to call back to the class
  124.     With iwp
  125.         ' Preprocess
  126.         If .MsgResponse = emrPreprocess Then
  127.             WindowProc = CallWindowProc(procOld, hWnd, iMsg, _
  128.                                         wParam, ByVal lParam)
  129.         End If
  130.         ' Consume
  131.         WindowProc = .WindowProc(hWnd, iMsg, wParam, ByVal lParam)
  132.         ' PostProcess
  133.         If .MsgResponse = emrPostProcess Then
  134.             WindowProc = CallWindowProc(procOld, hWnd, iMsg, _
  135.                                         wParam, ByVal lParam)
  136.         End If
  137.     End With
  138.     
  139. End Function
  140.  
  141. ' Cheat! Cut and paste from MWinTool rather than reusing
  142. ' file because reusing file would cause many unneeded dependencies
  143. Function IsWindowLocal(ByVal hWnd As Long) As Boolean
  144.     Dim idWnd As Long
  145.     Call GetWindowThreadProcessId(hWnd, idWnd)
  146.     IsWindowLocal = (idWnd = GetCurrentProcessId())
  147. End Function
  148. '
  149.  
  150.